// serial.h
//

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h> 

#include "../mcu/config.h"
#include "../mcu/types.h"
#include "../mcu/serial.h"


//////////////////////////////////////////////////////////////////////////////////////////////////////
//

int _uart_getchar(FILE *f);
int _uart_putchar(char c, FILE *f);


//////////////////////////////////////////////////////////////////////////////////////////////////////
//

volatile static u_char sio_rxd[MAX_SIO_COUNT];	// a pointer to serial Rx buffer

volatile static int sio_head;			//
volatile static int sio_tail;			//


//////////////////////////////////////////////////////////////////////////////////////////////////////
//

#if defined(__AVR_ATmega128__)
#ifdef USE_UART_ONE
ISR(USART1_RX_vect)
#else
ISR(USART0_RX_vect)
#endif	// USE_UART_ONE
#elif defined(__AVR_ATmega8__) || defined(__AVR_ATmega32__)
ISR(USART_RXC_vect)
#elif defined(__AVR_ATtiny2313__) || defined(__AVR_AT90S2313__)
ISR(USART_RX_vect)
#endif
{
	sio_rxd[sio_head] = UART_DATA;		// read RX data from UART I/O Data
	sio_head++;
	
	if( sio_head == sio_tail )
	{
		sio_head--;					// buffer full
	}

	if( sio_head >= MAX_SIO_COUNT )	// for ring buffer
	{
		sio_head = 0;

		if( sio_head == sio_tail ) 
			sio_head = MAX_SIO_COUNT;
	}
}

// Initialize UART
void uart_init(u_int baud_rate)
{
	UART_STATUSB	= (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);

	UART_BAUDRATEH	= (u_char)((baud_rate >> 8) & 0xFF);
	UART_BAUDRATEL	= (u_char)(baud_rate & 0xFF);

#ifdef USE_STANDARD_IO
	fdevopen((void *)_uart_putchar, (void *)_uart_getchar);
#endif // USE_STANDARD_IO

	// empty buffer
	sio_head = 0;
	sio_tail = 0;
}

void uart_databit(u_char databit)
{
	UART_STATUSB &= ~(1 << UCSZ2);
	UART_STATUSC &= ~((1 << UCSZ1) | (1 << UCSZ0));

	switch( databit )
	{
	case databit_5 :
		// nop
		break;
	case databit_6 :
		UART_STATUSC |= (1 << UCSZ0);
		break;
	case databit_7 :
		UART_STATUSC |= (1 << UCSZ1);
		break;
	case databit_8 :
		UART_STATUSC |= ((1 << UCSZ1) | (1 << UCSZ0));
		break;
	case databit_9 :
		UART_STATUSB |= (1 << UCSZ2);
		UART_STATUSC |= ((1 << UCSZ1) | (1 << UCSZ0));
		break;
	}
}

void uart_stopbit(u_char stopbit)
{
	switch( stopbit )
	{
	case stopbit_1 :
		UART_STATUSC &= ~(1 << USBS);
		break;
	case stopbit_2 :
		UART_STATUSC |= (1 << USBS);
		break;
	}
}

void uart_paritybit(u_char parity)
{
	switch( parity )
	{
	case parity_none :
		UART_STATUSC &= ~((1 << UPM1) | (1 << UPM0));
		break;
	case parity_even :
		UART_STATUSC |= (1 << UPM1);
		UART_STATUSC &= ~(1 << UPM0);
		break;
	case parity_odd  :
		UART_STATUSC |= ((1 << UPM1) | (1 << UPM0));
		break;
	}
}

u_int uart_keyhit(void)
{
	if( sio_head >= sio_tail ) 
		return (sio_head - sio_tail);

	return (MAX_SIO_COUNT - (sio_tail - sio_head));
}

int _uart_putchar(char c, FILE * f)
{
	while( ! (UART_STATUSA & (1 << UDRE)) );
	UART_DATA = c;

	return c;
}

int _uart_getchar(FILE * f)
{
	int c;

	if( sio_head == sio_tail )
		return -1;

	c = sio_rxd[sio_tail++];

	if( sio_tail >= MAX_SIO_COUNT ) 
		sio_tail = 0;

	return c;
}

int uart_putchar(char c)
{
	return _uart_putchar(c, NULL);
}

int uart_getchar()
{
	while( uart_keyhit() == 0 );

	return _uart_getchar(NULL);
}

void uart_puts(const char * str)
{
	while( *str )
		_uart_putchar(*str++, NULL);
}

void uart_puts_P(const prog_char * str)
{
	while( pgm_read_byte(str) )
		_uart_putchar(pgm_read_byte(str++), NULL);
}

int uart_gets(char * str, int max_len, char password)
{
	char * tsrc = str;
	char c;
	int len = 0;

	while( 1 )
	{ 
		if( (c = uart_getchar()) == 0x0D )
			break;

		if( c == 0x08 && tsrc != str )
		{
			uart_putchar(0x08);
			uart_putchar(' ');
			uart_putchar(0x08);

			str--;
			len--;

			continue;
		}
		else if( c == 0x1B )
		{
			while (tsrc != str)
			{
				uart_putchar(0x08);
				uart_putchar(' ');
				uart_putchar(0x08);

				str--;
				len--;
			}

			continue;			
		}
		else if( (c < 32 || c > 126) && c != '\t' )	
			continue; 

		if( len < max_len )
		{
			if(password) 
				uart_putchar('*');
			else
				uart_putchar(c);

			*str++ = c;
			len++;
		}
	}

	uart_putchar('\n');
	*str = '\0';
	
	return len;
}

void uart_flush_rx()
{
	sio_head = sio_tail;
}
